package model.shelves;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.naming.OperationNotSupportedException;

import model.events.ShelfAddedEvent;
import model.events.ShelfRemovedEvent;
import model.rentals.Rental;
import model.shelves.criteria.ICriterion;

/**
 * Sistema de pastas (shelves) para organizar os itens(rentals) alugados
 * @author DCO37
 * 	N.º 42484 Helder Ferreira
 *  N.º 45509 Luis Ferreira
 */
public class Shelves extends Observable implements IShelves{
	// lista de Shelves
	private List<Shelf> pastas ;
	
	private NormalShelf myRentals;
	
	/**
	 * Construtor de Shelves dado uma NormalShelf myRentals
	 * @param myRentals - NormalShelf
	 */
	public Shelves(NormalShelf myRentals) {
		
		pastas = new ArrayList<Shelf>();
		
		this.myRentals=myRentals;
	}

	@SuppressWarnings("unchecked")
	@Override
	/**
	 * Iterador de Shelves
	 */
	public Iterator<IShelf> iterator() {
		return (Iterator<IShelf>) pastas;
	}

	@Override
	/**
	 * Adiciona uma NormalShelf com um dado name
	 */
	public boolean addNormalShelf(String name) {
		pastas.add(new NormalShelf(name));
		setChanged();
		notifyObservers(new ShelfAddedEvent(name));
		return true ;
	}

	@Override
	/**
	 * * Adiciona uma SmartShelf dado um name e um criterion
	 */
	public boolean addSmartShelf(String name, ICriterion criterion) {
		pastas.add(new SmartShelf(name, myRentals, criterion));
		setChanged();
		notifyObservers(new ShelfAddedEvent(name));
		return true;
	}

	@Override
	/**
	 * Remove uma Shelf dado um name
	 */
	public void removeShelf(String name) {
		int i=0;
		boolean aux=true;
		while(aux && pastas.iterator().hasNext()){
			if (pastas.get(i).name.compareTo(name)==0){
				pastas.remove(i);
				setChanged();
				notifyObservers(new ShelfRemovedEvent(name));
			}
			else
				i++;
		}	
	}

	@Override
	/**
	 * Verifica se Shelf foi alugada
	 */
	public boolean isTheRentalShelf(String name) {
		return myRentals.iterator().equals(name); // como verificar se a pasta foi alugada ;// 
	}

	@Override
	/**
	 * Adiciona um rental a uma lista de rentals
	 */
	public boolean addRental(Rental rental) {
		try {
			return myRentals.addRental(rental);
		} catch (OperationNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return false;
	}

	@Override
	/**
	 * Adiciona um Rental a uma Shelf target
	 */
	public boolean addRentalToShelf(String target, Rental rental)
			throws OperationNotSupportedException {
		int i=0;
		boolean stop=true;
		while(stop && pastas.iterator().hasNext()) {
			if (pastas.get(i).getName().compareTo(target) == 0) {
				pastas.get(i).addRental(rental) ;
				stop=false;
				return true;
			}
			else
				i++;
		}
		return false;
	}

	@Override
	/**
	 * Remove um rental dado o nome da Shelf e esse rental
	 */
	public void removeRentalFromShelf(String name, Rental rental)
			throws OperationNotSupportedException {
		int i=0;
		boolean stop=true;
		while(stop && pastas.iterator().hasNext()) {
			
			if (pastas.get(i).getName().compareTo(name) == 0) {
				pastas.get(i).addRental(rental) ;
				stop=false;
			}
			else
				i++;
		}
	}

	@Override
	/**
	 * Iterador de rentals
	 */
	public Iterable<Rental> getRentals() {
		return myRentals;
	}

	@Override
	/**
	 * Iterador de Shelfs dado uma shelfName
	 */
	public Iterable<Rental> getShelfRentals(String shelfName) {
		return pastas.get((pastas.indexOf(shelfName)));
	}

	@Override
	/**
	 * Verifica se rental ja foi alugada
	 */
	public boolean isRented(Rental rental) {
		boolean aux=false;
		for(Rental r: myRentals){
			if(r.equals(rental))
				aux = true;	
		}

		return aux;
	}

	@Override
	/**
	 * Verifica se rental ja expirou o prazo de aluguer
	 */
	public boolean isExpired(Rental rental) {
		return rental.isExpired();
	}

	@Override
	/**
	 * Adiciona um observer que notifica se um utente adiciona ou 
	 * remove uma shelf
	 */
	public void addShelfCollectionObserver(Observer observer) {
		myRentals.addObserver(observer);	
	}

	@Override
	/**
	 * Remove um observer que notificava  se um utente adicionava ou 
	 * removia uma shelf
	 */
	public void removeShelfCollectionObserver(Observer observer) {
		myRentals.deleteObserver(observer);
	}

	@Override
	/**
	 * Adiciona um observer que notifica quando um e-medium e adicionado 
	 * ou removido de uma shelf com um shelfName
	 */
	public void addRentalCollectionObserver(String shelfName, Observer observer) {
		int i=0;
		boolean aux=true;
		
		while(aux && pastas.iterator().hasNext()){
			if(pastas.get(i).getName().compareTo(shelfName)==0){
				pastas.get(i).addObserver(observer);
				aux=false;
			}
			else
				i++;
		}		
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((myRentals == null) ? 0 : myRentals.hashCode());
		result = prime * result + ((pastas == null) ? 0 : pastas.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Shelves))
			return false;
		Shelves other = (Shelves) obj;
		if (myRentals == null) {
			if (other.myRentals != null)
				return false;
		} else if (!myRentals.equals(other.myRentals))
			return false;
		if (pastas == null) {
			if (other.pastas != null)
				return false;
		} else if (!pastas.equals(other.pastas))
			return false;
		return true;
	}

	@Override
	/**
	 * Remove um observer que notificava se um utente adicionava ou 
	 * removia uma e-medium de uma Shelf com um ShelfName
	 */
	public void removeRentalCollectionObserver(String shelfName,
			Observer observer) {
		int i=0;
		boolean aux=true;
		
		while(aux && pastas.iterator().hasNext()){
			if(pastas.get(i).getName().compareTo(shelfName)==0){
				pastas.get(i).deleteObserver(observer);
				aux=false;
			}
			else
				i++;
		}	
	}
}
